package com.yunchao.library.device

import android.content.Context
import com.seuic.psam.PsamService
import com.seuic.uhf.EPC
import com.seuic.uhf.UHFService
import com.seuic.uhf.UHFService.PARAMETER_TAG_FILTER
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.isActive
import kotlinx.coroutines.withContext
import kotlinx.coroutines.sync.withLock
import com.yunchao.library.lock.DeviceHelper
import com.yunchao.library.lock.IDeviceHelper.Companion.LOOP_SCAN_POWER
import com.yunchao.library.lock.IDeviceHelper.Companion.PSAM_AUTH_ERROR_MESSAGE
import com.yunchao.library.lock.IDeviceHelper.Companion.PSAM_DEFAULT_HEX_DATA
import com.yunchao.library.lock.IDeviceHelper.Companion.PSAM_SUCCEED_INFO
import com.yunchao.library.lock.IDeviceHelper.Companion.READ_DEFAULT_PASSWORD
import com.yunchao.library.lock.IDeviceHelper.Companion.READ_WRITE_POWER
import com.yunchao.library.lock.IDeviceHelper.Companion.RFID_INVENTORY_ONCE_LOCK_MESSAGE
import com.yunchao.library.lock.IDeviceHelper.Companion.RFID_POWER_ERROR_MESSAGE
import com.yunchao.library.lock.IDeviceHelper.Companion.RFID_READ_LOCK_INFO_MESSAGE
import com.yunchao.library.lock.IDeviceHelper.Companion.RFID_SET_INFO_ERROR_MESSAGE
import com.yunchao.library.lock.IDeviceHelper.Companion.RFID_START_LOOP_SCAN_MESSAGE
import com.yunchao.library.lock.IDeviceHelper.Companion.RFID_STOP_LOOP_SCAN_MESSAGE
import com.yunchao.library.lock.IDeviceHelper.Companion.SINGLE_SCAN_POWER
import com.yunchao.library.lock.LockAction
import com.yunchao.library.lock.LockEPCInfo
import com.yunchao.library.lock.byteArrayToHexString
import com.yunchao.library.lock.checkLock
import com.yunchao.library.lock.getAuthLockEPC
import com.yunchao.library.lock.getLockEPCInfo
import com.yunchao.library.lock.hexStringToByteArray
import timber.log.Timber

class DeviceHelperImpl(context: Context) : DeviceHelper() {

    private val mDevice: UHFService by lazy { UHFService.getInstance(context.applicationContext) }
    private val psamService: PsamService by lazy { PsamService.getInstance() }

    companion object {
        private const val INVENTORY_ONCE_TIMEOUT = 100
    }

    override suspend fun openDevice(): Boolean = withContext(Dispatchers.Default) {
        val deviceOpenResult = async { mDevice.isOpen || mDevice.open() }.await()
        val deviceHidePC =
            async { mDevice.setParameters(UHFService.PARAMETER_HIDE_PC, 1) }.await()
        val psamOpenResult = async { openPSAM() }.await()
        deviceOpenResult && deviceHidePC && psamOpenResult
    }

    override suspend fun openEmbededDevice(): Boolean = withContext(Dispatchers.Default) {
        val deviceOpenResult = async { mDevice.isOpen || mDevice.open() }.await()
        val deviceHidePC =
            async { mDevice.setParameters(UHFService.PARAMETER_HIDE_PC, 1) }.await()
        val embededDataResult = async {
            mDevice.setParamBytes(
                UHFService.PARAMETER_TAG_EMBEDEDDATA,
                createEmbededData()
            )
        }.await()
        val psamOpenResult = async { openPSAM() }.await()

        deviceOpenResult && deviceHidePC && embededDataResult && psamOpenResult
    }

    override suspend fun closeDevice() = withContext(Dispatchers.Default) {
        mDevice.close()
        psamService.close()
    }

    override suspend fun inventoryOnce(): LockEPCInfo = withContext(Dispatchers.Default) {
        check(mDevice.setPower(SINGLE_SCAN_POWER)) { RFID_POWER_ERROR_MESSAGE }
        val epc = EPC()
        val result = async { mDevice.inventoryOnce(epc, INVENTORY_ONCE_TIMEOUT) }.await()
        check(result) { RFID_INVENTORY_ONCE_LOCK_MESSAGE }
        try {
            getLockEPCInfo(epc.getId(), tId = epc.embeded)
        } catch (e: Exception) {
            Timber.e(e, "解析 EPC 信息时发生异常   $epc ")
            throw IllegalStateException("解析 EPC 信息时发生异常   $epc ")
        }
    }

    override suspend fun readLockErrorCode(): LockEPCInfo = withContext(Dispatchers.Default) {
        check(mDevice.setPower(SINGLE_SCAN_POWER)) { RFID_POWER_ERROR_MESSAGE }
        val epc = EPC()
        val result = async { mDevice.inventoryOnce(epc, INVENTORY_ONCE_TIMEOUT) }.await()
        check(result) { RFID_INVENTORY_ONCE_LOCK_MESSAGE }

        val errorCode =
            readTagData(
                epcId = epc.getId(),
                password = READ_DEFAULT_PASSWORD,
                offset = 74 * 2,
                len = 4,
                bank = 3
            )

        check(authPSAM()) { PSAM_AUTH_ERROR_MESSAGE }


        val lockAuthResult =
            sendApdu(data = getAuthLockEPC(epcStr = epc.getId(), epcTid = epc.embeded))

        try {
            getLockEPCInfo(
                epc.getId(),
                tId = epc.embeded,
                lockAuthResult = lockAuthResult,
                lockErrorCode = errorCode
            )
        } catch (e: Exception) {
            Timber.e(e, "解析 EPC 信息时发生异常   $epc ")
            throw IllegalStateException("解析 EPC 信息时发生异常   $epc ")
        }
    }

    override fun inventoryLoop(): Flow<List<LockEPCInfo>> = flow {
        check(mDevice.setPower(LOOP_SCAN_POWER)) { RFID_POWER_ERROR_MESSAGE }

        check(mDevice.setParameters(UHFService.PARAMETER_CLEAR_EPCLIST_WHEN_START_INVENTORY, 1)) { RFID_SET_INFO_ERROR_MESSAGE }

        check(mDevice.inventoryStart()) { RFID_START_LOOP_SCAN_MESSAGE }

        try {
            while (currentCoroutineContext().isActive) {
                val result = runCatching {
                    mDevice.tagIDs
                        .filter { it.getId().isNotBlank() }
                        .map { getLockEPCInfo(it.getId()) }
                }.getOrDefault(emptyList())

                emit(result)
                delay(100)
            }
        } finally {
            check(mDevice.inventoryStop()) { RFID_STOP_LOOP_SCAN_MESSAGE }
        }
    }


    override suspend fun inventoryStop(): Boolean = withContext(Dispatchers.Default) {
        mDevice.setPower(SINGLE_SCAN_POWER)
    }

    override suspend fun setPower(power: Int): Boolean = withContext(Dispatchers.Default) {
        mDevice.setPower(power)
    }

    override suspend fun readTagData(
        epcId: String,
        password: String,
        bank: Int,
        offset: Int,
        len: Int
    ): String = withContext(Dispatchers.Default) {

        check(mDevice.setPower(READ_WRITE_POWER)) { RFID_POWER_ERROR_MESSAGE }

        Timber.i("读取锁信息时传入参数：  epcId = $epcId, password = $password, bank = $bank, offset = $offset , len = $len")

        val buffer = ByteArray(len)
        val result = mDevice.readTagData(
            epcId.hexStringToByteArray(),
            password.hexStringToByteArray(),
            bank, offset, len, buffer
        )
        check(result) { RFID_READ_LOCK_INFO_MESSAGE }

        val readInfo = buffer.byteArrayToHexString()

        Timber.i("读取锁信息时返回结果：  $readInfo")
        readInfo
    }

    override suspend fun writeTagData(
        epcId: String,
        password: String,
        bank: Int,
        offset: Int,
        len: Int,
        data: String
    ): Boolean = withContext(Dispatchers.Default) {
        check(mDevice.setPower(READ_WRITE_POWER)) { RFID_POWER_ERROR_MESSAGE }

        Timber.i("写入指令时传入参数：  epcId = $epcId, password = $password, bank = $bank, offset = $offset, len = $len")

        val result = mDevice.writeTagData(
            epcId.hexStringToByteArray(),
            password.hexStringToByteArray(),
            bank, offset, len, data.hexStringToByteArray()
        )

        Timber.i("写入指令时返回结果：  $result")

        result
    }


    override suspend fun verifyLockStatus(
        epcId: String,
        password: String,
        action: LockAction
    ): LockEPCInfo = withContext(Dispatchers.Default) {
        Timber.d("验证锁状态时传入参数：  epcId = $epcId, password = $password, action = $action")
        check(
            mDevice.setParamBytes(
                PARAMETER_TAG_FILTER,
                getTagFilterData(epcId.substring(0, 8))
            )
        ) {
            RFID_SET_INFO_ERROR_MESSAGE
        }
        val epc = EPC()
        val result = async { mDevice.inventoryOnce(epc, INVENTORY_ONCE_TIMEOUT) }.await()

        Timber.d("验证锁状态时返回结果：  $epc")

        check(result) { RFID_READ_LOCK_INFO_MESSAGE }

        mDevice.setParamBytes(PARAMETER_TAG_FILTER, null)

        try {
            val result = getLockEPCInfo(epc.getId(), tId = epc.embeded)
            result.checkLock(action)
        } catch (e: Exception) {
            Timber.e(e, "解析 EPC 信息时发生异常   $epc ")
            throw e
        }
    }

    override suspend fun openPSAM(): Boolean =
        psamService.isCardExist || (psamService.open() && psamService.isCardExist)

    override suspend fun psamReset(): String? {
        return if (openPSAM()) {
            psamService.reset()?.byteArrayToHexString()
        } else null
    }

    override suspend fun authPSAM(): Boolean = openPSAM() && psamService.reset()?.run {
        sendApdu("", PSAM_DEFAULT_HEX_DATA) == PSAM_SUCCEED_INFO
    } == true

    override suspend fun sendApdu(cmd: String, data: String): String = psamMutex.withLock {

        Timber.i("PSAM 发送指令时传入参数：  cmd = $cmd, data = $data")

        val result = psamService.sendApdu(data.hexStringToByteArray())
            ?.byteArrayToHexString()
            ?.uppercase()
            .orEmpty()

        Timber.i("PSAM 发送指令时返回结果：  $result")
        result
    }

    private fun getTagFilterData(fDataStr: String): ByteArray = ByteArray(255).apply {
        this[0] = 1
        this[1] = 0
        this[2] = 4
        this[3] = 0
        fDataStr.hexStringToByteArray().copyInto(this, 4, 0, 4)
    }

    private fun createEmbededData(): ByteArray = ByteArray(255).apply {
        this[0] = 2.toByte()
        this[1] = 0.toByte()
        this[2] = 12.toByte()
        READ_DEFAULT_PASSWORD.hexStringToByteArray()
            .copyInto(this, destinationOffset = 3, startIndex = 0, endIndex = 4)
    }
}
